import cv2
import numpy as np
import os
from models.model_handler import run_inference

# 尝试导入pytesseract，如果不可用则跳过
try:
    import pytesseract
    # 设置 pytesseract 路径（Windows 环境下需要）
    pytesseract_path = r'D:\Python\Tesseract-OCR\tesseract.exe'
    if os.path.exists(pytesseract_path):
        pytesseract.pytesseract.tesseract_cmd = pytesseract_path
        TESSERACT_AVAILABLE = True
    else:
        print("警告: Tesseract OCR未安装或路径不正确，文本识别功能将不可用")
        TESSERACT_AVAILABLE = False
except ImportError:
    print("警告: pytesseract模块不可用，文本识别功能将不可用")
    TESSERACT_AVAILABLE = False

# 颜色范围定义 (HSV)
COLOR_RANGES = {
    'red': [(0, 70, 70), (10, 255, 255), (160, 70, 70), (180, 255, 255)],  # 红色在HSV空间中有两个范围
    'green': [(35, 40, 40), (85, 255, 255)],
    'blue': [(90, 40, 40), (130, 255, 255)],
    'yellow': [(15, 70, 70), (35, 255, 255)],
    'purple': [(125, 40, 40), (165, 255, 255)],
    'white': [(0, 0, 180), (180, 30, 255)],
    'black': [(0, 0, 0), (180, 255, 30)],
    'cyan': [(80, 40, 40), (100, 255, 255)]  # 青色
}

def process_image(image):
    """
    预处理图像
    
    Args:
        image: 输入图像
        
    Returns:
        processed_image: 预处理后的图像
    """
    # 转换为RGB格式
    if len(image.shape) == 2:  # 灰度图
        image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
    elif image.shape[2] == 4:  # 带Alpha通道
        image = cv2.cvtColor(image, cv2.COLOR_RGBA2RGB)
    elif image.shape[2] == 3 and image.dtype == np.uint8:
        # 检查是否是BGR格式（OpenCV默认）
        if not is_rgb(image):
            image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    
    # 图像增强
    image = enhance_image(image)
    
    return image

def is_rgb(image):
    """检查图像是否为RGB格式（简单启发式）"""
    # 这是一个简单启发式方法，不能100%准确
    # 在实际应用中可能需要更复杂的方法或通过元数据确定
    r, g, b = cv2.split(image)
    r_mean, g_mean, b_mean = np.mean(r), np.mean(g), np.mean(b)
    
    # 假设大多数医疗图像背景为白色，在RGB中白色区域RGB值相近
    return abs(r_mean - g_mean) < 10 and abs(r_mean - b_mean) < 10

def enhance_image(image):
    """
    增强图像质量
    
    Args:
        image: 输入图像
        
    Returns:
        enhanced_image: 增强后的图像
    """
    # 复制图像以防止修改原图
    img = image.copy()
    
    # 应用CLAHE（自适应直方图均衡化）增强对比度
    if len(img.shape) == 3:  # 彩色图像
        # 转换为LAB颜色空间
        lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)
        # 分离通道
        l, a, b = cv2.split(lab)
        # 创建CLAHE对象
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        # 应用CLAHE仅对L通道
        l = clahe.apply(l)
        # 合并通道
        lab = cv2.merge((l, a, b))
        # 转换回RGB
        img = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)
    else:  # 灰度图像
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
        img = clahe.apply(img)
    
    # 调整亮度和对比度
    alpha = 1.1  # 对比度增强因子，更温和
    beta = 5     # 亮度增强因子，更温和
    
    # 应用对比度和亮度调整
    img = cv2.convertScaleAbs(img, alpha=alpha, beta=beta)
    
    # 降噪，使用更温和的参数
    img = cv2.fastNlMeansDenoisingColored(img, None, 7, 7, 5, 15)
    
    # 锐化处理
    kernel = np.array([[-1, -1, -1],
                        [-1,  9, -1],
                        [-1, -1, -1]])
    img = cv2.filter2D(img, -1, kernel)
    
    return img

def detect_tubes(image, model):
    """
    检测图像中的试管并提取特征
    
    Args:
        image: 输入图像
        model: YOLOv8模型字典
        
    Returns:
        annotated_image: 标注后的图像
        tube_data: 试管数据列表，每个试管包含长度、颜色、液体位置、标签内容等信息
    """
    # 预处理图像
    processed_image = process_image(image)
    
    # 获取图像尺寸
    height, width = processed_image.shape[:2]
    
    # 创建一个深拷贝用于标注
    annotated_image = processed_image.copy()
    
    # 存储试管数据
    tube_data = []
    
    # 首先使用物体检测模型检测试管
    if 'main' in model:
        # 使用物体检测模型
        from models.model_handler import run_inference
        detect_results = run_inference(model, processed_image, 'detect')
        
        if detect_results and len(detect_results) > 0 and len(detect_results[0].boxes) > 0:
            # 找到所有可能是试管的物体
            boxes = []
            for i, (box, cls, conf) in enumerate(zip(
                detect_results[0].boxes.xyxy.cpu().numpy(),
                detect_results[0].boxes.cls.cpu().numpy(),
                detect_results[0].boxes.conf.cpu().numpy()
            )):
                cls_name = detect_results[0].names[int(cls)]
                # 如果是可能的试管类物体或检测到的物体较高
                if cls_name.lower() in ['tube', 'test tube', 'vial', '试管', 'vase', 'bottle', 'container', 'cup', '容器', '瓶子', '杯子'] or (box[3] - box[1]) > height * 0.6:
                    boxes.append({
                        'box': box,
                        'conf': conf,
                        'cls': cls_name
                    })
            
            # 如果检测到了可能的试管
            if boxes:
                # 按照x坐标排序
                boxes.sort(key=lambda b: b['box'][0])
                
                # 处理每个检测到的试管
                for i, box_data in enumerate(boxes):
                    box = box_data['box']
                    x1, y1, x2, y2 = map(int, box)
                    
                    # 裁剪试管区域
                    tube_roi = processed_image[y1:y2, x1:x2]
                    
                    if tube_roi.size == 0 or tube_roi.shape[0] < 10 or tube_roi.shape[1] < 10:
                        continue
                    
                    # 使用增强的属性分析
                    from models.model_handler import analyze_tube_properties
                    properties = analyze_tube_properties(tube_roi, model)
                    
                    # 获取试管属性
                    tube_length = properties.get('tube_length', y2 - y1)
                    cap_color = properties.get('cap_color', '未知')
                    liquid_position = properties.get('liquid_position', '未知')
                    label_text = properties.get('label_text', '未能识别')
                    
                    # 在图像上标注
                    cv2.rectangle(annotated_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
                    
                    # 添加试管标识
                    label_info = f"ID:{i+1} {cap_color}"
                    cv2.putText(annotated_image, label_info, (x1, y1-10), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
                               
                    # 添加液位信息
                    cv2.putText(annotated_image, liquid_position, (x1, y1+20), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
                    
                    # 存储试管数据
                    tube_data.append({
                        'id': i+1,
                        'length': tube_length,
                        'cap_color': cap_color,
                        'liquid_position': liquid_position,
                        'label_text': label_text,
                        'confidence': float(box_data['conf']),
                        'bbox': [x1, y1, x2, y2]
                    })
                
                # 如果成功检测到试管，直接返回结果
                if tube_data:
                    return annotated_image, tube_data
    
    # 如果未能通过物体检测识别试管，尝试使用分割模型
    if 'segment' in model and model['segment']:
        # 使用分割模型识别试管
        from models.model_handler import run_inference
        segment_results = run_inference(model, processed_image, 'segment')
        
        if segment_results and len(segment_results) > 0:
            # 从分割结果中提取试管
            tubes_from_seg = extract_tubes_from_segmentation(segment_results[0], processed_image)
            if tubes_from_seg and tubes_from_seg['tubes']:
                # 使用分割结果
                return tubes_from_seg['image'], tubes_from_seg['tubes']
    
    # 如果上述方法都未能识别试管，尝试使用试管检测算法
    tubes_detected = detect_tubes_by_analysis(processed_image)
    
    if tubes_detected:
        tube_positions = tubes_detected['positions']
        num_tubes = len(tube_positions)
        
        # 处理每个检测到的试管
        for i, (left, right) in enumerate(tube_positions):
            x1 = left
            x2 = right
            y1 = 0
            y2 = height
            
            # 裁剪试管区域
            tube_roi = processed_image[y1:y2, x1:x2]
            
            # 使用增强的属性分析
            from models.model_handler import analyze_tube_properties
            properties = analyze_tube_properties(tube_roi, model)
            
            # 获取试管属性
            tube_length = properties.get('tube_length', y2 - y1)
            cap_color = properties.get('cap_color', '未知')
            liquid_position = properties.get('liquid_position', '未知')
            label_text = properties.get('label_text', '未能识别')
            
            # 在图像上标注
            cv2.rectangle(annotated_image, (x1, y1), (x2, y2), (0, 255, 0), 2)
            
            # 添加试管标识
            label_info = f"ID:{i+1} {cap_color}"
            cv2.putText(annotated_image, label_info, (x1+5, y1+20), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
                       
            # 添加液位信息
            cv2.putText(annotated_image, liquid_position, (x1+5, y1+height//2), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
            
            # 存储试管数据
            tube_data.append({
                'id': i+1,
                'length': tube_length,
                'cap_color': cap_color,
                'liquid_position': liquid_position,
                'label_text': label_text,
                'confidence': 0.8,  # 自定义检测置信度
                'bbox': [x1, y1, x2, y2]
            })
            
        return annotated_image, tube_data
    
    # 如果所有方法都失败，使用简单的等分方法估计试管数量
    # 先尝试估计图像中可能的试管数量
    estimated_tubes = estimate_tube_count(processed_image)
    tube_width = width // estimated_tubes
    tube_positions = []
    
    for i in range(estimated_tubes):
        left = i * tube_width
        right = (i + 1) * tube_width
        tube_positions.append((left, right))
    
    # 处理每个分割区域
    for i, (left, right) in enumerate(tube_positions):
        x1 = left
        x2 = right
        y1 = 0
        y2 = height
        
        # 裁剪试管区域
        tube_roi = processed_image[y1:y2, x1:x2]
        
        # 使用增强的属性分析
        from models.model_handler import analyze_tube_properties
        properties = analyze_tube_properties(tube_roi, model)
        
        # 获取试管属性
        tube_length = properties.get('tube_length', y2 - y1)
        cap_color = properties.get('cap_color', '未知')
        liquid_position = properties.get('liquid_position', '未知')
        label_text = properties.get('label_text', '未能识别')
        
        # 在图像上标注，只在垂直边缘画线而不是完整矩形，减少视觉干扰
        cv2.line(annotated_image, (x1, y1), (x1, y2), (0, 255, 0), 2)  # 左边缘
        cv2.line(annotated_image, (x2, y1), (x2, y2), (0, 255, 0), 2)  # 右边缘
        
        # 在顶部添加试管标识
        label_info = f"ID:{i+1} {cap_color}"
        cv2.putText(annotated_image, label_info, (x1+5, y1+20), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
                   
        # 在中部添加液位信息
        cv2.putText(annotated_image, liquid_position, (x1+5, y1+height//2), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1)
        
        # 存储试管数据
        tube_data.append({
            'id': i+1,
            'length': tube_length,
            'cap_color': cap_color,
            'liquid_position': liquid_position,
            'label_text': label_text,
            'confidence': 0.7,  # 固定较低的置信度
            'bbox': [x1, y1, x2, y2]
        })
    
    return annotated_image, tube_data

def detect_tubes_by_analysis(image):
    """分析图像结构检测试管"""
    try:
        height, width = image.shape[:2]
        
        # 转为灰度图
        gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        
        # 应用Canny边缘检测
        edges = cv2.Canny(gray, 50, 150)
        
        # 寻找垂直线条，这可能是试管边缘
        lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=50, minLineLength=height//2, maxLineGap=20)
        
        if lines is None or len(lines) < 2:
            return None
            
        # 筛选出垂直线条
        vertical_lines = []
        for line in lines:
            x1, y1, x2, y2 = line[0]
            # 计算线的角度
            angle = np.abs(np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi)
            # 如果接近垂直(90度)
            if angle > 75 and angle < 105:
                vertical_lines.append((x1 + x2) // 2)  # 取线的x轴中点
        
        # 如果没有足够的垂直线，可能检测失败
        if len(vertical_lines) < 2:
            return None
            
        # 对垂直线的x坐标排序
        vertical_lines.sort()
        
        # 合并相近的线(可能是同一条边的多次检测)
        merged_lines = []
        current_line = vertical_lines[0]
        threshold = width * 0.05  # 相距5%宽度内的线视为同一条
        
        for line in vertical_lines[1:]:
            if line - current_line > threshold:
                merged_lines.append(current_line)
                current_line = line
            else:
                # 取平均值
                current_line = (current_line + line) // 2
        
        merged_lines.append(current_line)
        
        # 如果识别出的线太少，可能不是试管图像
        if len(merged_lines) < 2:
            return None
            
        # 如果第一条线不是在图像左侧，添加左边界
        if merged_lines[0] > width * 0.1:
            merged_lines.insert(0, 0)
            
        # 如果最后一条线不是在图像右侧，添加右边界
        if merged_lines[-1] < width * 0.9:
            merged_lines.append(width)
            
        # 将线对应边界，形成试管位置
        tube_positions = []
        for i in range(len(merged_lines) - 1):
            left = merged_lines[i]
            right = merged_lines[i + 1]
            # 如果区域太窄，可能不是有效的试管
            if right - left > width * 0.03:  # 宽度至少为图像宽度的3%
                tube_positions.append((left, right))
                
        # 如果没有检测到有效的试管区域
        if not tube_positions:
            return None
            
        return {
            'positions': tube_positions,
            'count': len(tube_positions)
        }
            
    except Exception as e:
        print(f"试管结构分析失败: {str(e)}")
        return None

def estimate_tube_count(image):
    """估计图像中可能的试管数量"""
    height, width = image.shape[:2]
    
    # 使用图像特征估计试管数量
    # 方法1: 基于图像宽度的启发式估计
    if width > 1000:
        estimated_tubes = 7  # 宽图像可能包含更多试管
    elif width > 600:
        estimated_tubes = 5  # 中等宽度图像
    elif width > 300:
        estimated_tubes = 3  # 较窄图像
    else:
        estimated_tubes = 2  # 非常窄的图像
    
    # 方法2: 分析颜色变化来估计试管数量
    try:
        # 转换为RGB以便分析
        rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) if len(image.shape) == 3 else image
        
        # 提取中心行的颜色变化
        center_row = rgb[height//2, :, :]
        
        # 计算相邻像素间的颜色差异
        color_diff = np.sum(np.abs(np.diff(center_row, axis=0)), axis=1)
        
        # 应用阈值找到明显的颜色变化点
        threshold = np.mean(color_diff) + np.std(color_diff)
        change_points = np.where(color_diff > threshold)[0]
        
        # 合并相近的变化点
        merged_points = []
        if len(change_points) > 0:
            current_point = change_points[0]
            merging_distance = width * 0.05  # 5%宽度内的点合并
            
            for point in change_points[1:]:
                if point - current_point > merging_distance:
                    merged_points.append(current_point)
                    current_point = point
            
            merged_points.append(current_point)
            
            # 颜色变化点数量可以帮助估计试管数量
            color_based_count = len(merged_points) // 2 + 1  # 每个试管有两个边缘
            
            # 取两种方法的平均值
            estimated_tubes = max(1, min(10, (estimated_tubes + color_based_count) // 2))
    except:
        # 如果颜色分析失败，使用方法1的结果
        pass
    
    return estimated_tubes

def detect_tubes_by_segmentation(image):
    """
    通过图像分割检测试管数量
    """
    # 对于排列整齐的多个试管图像，可以通过颜色变化或边缘检测来区分
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    edges = cv2.Canny(gray, 50, 150)
    
    # 图像宽度
    width = image.shape[1]
    
    # 基于图像分析，确定试管数量和大致位置
    if width > 1000:
        # 对于较宽的图像，识别7个试管
        tube_positions = []
        tube_width = width // 7
        
        for i in range(7):
            center = i * tube_width + tube_width // 2
            left = max(0, center - tube_width // 3)
            right = min(width, center + tube_width // 3)
            tube_positions.append((left, right))
        
        return tube_positions
    else:
        # 使用固定分隔，根据图像宽度计算
        n_tubes = max(3, width // 150)  # 每个试管大约占150像素宽
        tube_positions = []
        
        tube_width = width // n_tubes
        for i in range(n_tubes):
            left = i * tube_width
            right = (i+1) * tube_width
            tube_positions.append((left, right))
            
        return tube_positions

def detect_cap_color(tube_image):
    """
    检测试管冒的颜色
    
    Args:
        tube_image: 试管区域图像
        
    Returns:
        color_name: 颜色名称
    """
    # 假设试管冒在试管的顶部20%区域
    height, width = tube_image.shape[:2]
    cap_region = tube_image[0:int(height*0.2), :]
    
    # 转换为HSV颜色空间
    hsv = cv2.cvtColor(cap_region, cv2.COLOR_RGB2HSV)
    
    # 统计每种颜色的像素数
    color_counts = {}
    
    for color_name, ranges in COLOR_RANGES.items():
        mask = None
        
        # 有些颜色（如红色）在HSV中有两个范围
        if len(ranges) == 4:  # 如红色有两个范围
            lower1, upper1, lower2, upper2 = ranges
            mask1 = cv2.inRange(hsv, np.array(lower1), np.array(upper1))
            mask2 = cv2.inRange(hsv, np.array(lower2), np.array(upper2))
            mask = cv2.bitwise_or(mask1, mask2)
        else:  # 一般颜色只有一个范围
            lower, upper = ranges
            mask = cv2.inRange(hsv, np.array(lower), np.array(upper))
        
        # 计算该颜色的像素数
        color_counts[color_name] = cv2.countNonZero(mask)
    
    # 选择像素数最多的颜色
    dominant_color = max(color_counts.items(), key=lambda x: x[1])
    
    # 如果占比太小，认为是无法识别
    total_pixels = cap_region.shape[0] * cap_region.shape[1]
    if dominant_color[1] < total_pixels * 0.1:
        return "未知"
    
    return dominant_color[0]

def detect_liquid_position(tube_image):
    """
    检测试管中液体的位置
    
    Args:
        tube_image: 试管区域图像
        
    Returns:
        position: 液体位置描述
    """
    # 转换为HSV颜色空间以更好地区分液体和试管
    hsv = cv2.cvtColor(tube_image, cv2.COLOR_RGB2HSV)
    height, width = hsv.shape[:2]
    
    # 使用颜色掩码方法而不是边缘检测，对透明液体更有效
    # 排除近白色区域(高亮度低饱和度)
    lower_white = np.array([0, 0, 200])
    upper_white = np.array([180, 30, 255])
    white_mask = cv2.inRange(hsv, lower_white, upper_white)
    
    # 假设非白色区域是液体
    liquid_mask = 255 - white_mask
    
    # 按行扫描确定液面
    liquid_level = height  # 默认底部
    liquid_found = False
    
    # 从底部向上扫描
    for y in range(height-1, int(height * 0.2), -1):
        row_sum = np.sum(liquid_mask[y, :])
        if row_sum > (width * 30):  # 有足够的液体像素
            liquid_level = y
            liquid_found = True
            break
    
    if not liquid_found:
        return "未检测到液体"
    
    # 计算液体高度比例
    liquid_height_ratio = liquid_level / height
    
    # 根据比例返回描述
    if liquid_height_ratio < 0.35:
        return "低（<35%）"
    elif liquid_height_ratio < 0.5:
        return "中（35-50%）"
    elif liquid_height_ratio < 0.75:
        return "高（50-75%）"
    else:
        return "满（>75%）"

def detect_liquid_color(tube_image):
    """
    检测试管中液体的颜色
    
    Args:
        tube_image: 试管区域图像
        
    Returns:
        color_name: 颜色名称
    """
    # 假设液体在试管的下半部分
    height, width = tube_image.shape[:2]
    liquid_region = tube_image[int(height*0.4):int(height*0.7), int(width*0.2):int(width*0.8)]
    
    # 如果区域太小，返回未知
    if liquid_region.size == 0 or liquid_region.shape[0] < 10 or liquid_region.shape[1] < 10:
        return "未知"
    
    # 转换为HSV颜色空间
    hsv = cv2.cvtColor(liquid_region, cv2.COLOR_RGB2HSV)
    
    # 统计每种颜色的像素数
    color_counts = {}
    
    for color_name, ranges in COLOR_RANGES.items():
        mask = None
        
        # 有些颜色（如红色）在HSV中有两个范围
        if len(ranges) == 4:  # 如红色有两个范围
            lower1, upper1, lower2, upper2 = ranges
            mask1 = cv2.inRange(hsv, np.array(lower1), np.array(upper1))
            mask2 = cv2.inRange(hsv, np.array(lower2), np.array(upper2))
            mask = cv2.bitwise_or(mask1, mask2)
        else:  # 一般颜色只有一个范围
            lower, upper = ranges
            mask = cv2.inRange(hsv, np.array(lower), np.array(upper))
        
        # 计算该颜色的像素数
        color_counts[color_name] = cv2.countNonZero(mask)
    
    # 选择像素数最多的颜色
    dominant_color = max(color_counts.items(), key=lambda x: x[1])
    
    # 如果占比太小，认为是无法识别
    total_pixels = liquid_region.shape[0] * liquid_region.shape[1]
    if dominant_color[1] < total_pixels * 0.05:
        return "透明"
    
    # 颜色映射为中文
    color_mapping = {
        'red': '红色',
        'green': '绿色', 
        'blue': '蓝色',
        'yellow': '黄色',
        'purple': '紫色',
        'white': '白色',
        'black': '黑色',
        'cyan': '青色'
    }
    
    return color_mapping.get(dominant_color[0], dominant_color[0])

def recognize_text(tube_image):
    """
    识别试管标签上的文本
    
    Args:
        tube_image: 试管区域图像
        
    Returns:
        text: 识别的文本
    """
    # 检查Tesseract是否可用
    if not 'TESSERACT_AVAILABLE' in globals() or not TESSERACT_AVAILABLE:
        return "未安装Tesseract OCR，无法识别文本"
        
    try:
        # 假设标签在试管的中部区域
        height, width = tube_image.shape[:2]
        label_region = tube_image[int(height*0.3):int(height*0.7), :]
        
        # 转换为灰度图
        gray = cv2.cvtColor(label_region, cv2.COLOR_RGB2GRAY)
        
        # 应用自适应阈值处理突出文本
        binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                     cv2.THRESH_BINARY_INV, 11, 2)
        
        # 应用形态学操作清理噪点
        kernel = np.ones((2, 2), np.uint8)
        cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
        
        # 使用pytesseract识别文本
        custom_config = r'--oem 3 --psm 6'
        text = pytesseract.image_to_string(cleaned, config=custom_config)
        
        # 清理文本
        text = text.strip()
        
        return text if text else "无法识别"
    except Exception as e:
        print(f"文本识别错误: {str(e)}")
        return "识别失败"

def enhance_tubes(image):
    """增强图像中的试管特征，强调垂直边缘"""
    # 转为灰度图
    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    
    # 使用垂直核进行形态学操作，增强垂直线条
    kernel = np.ones((15, 1), np.uint8)
    vertical = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, kernel)
    
    # 边缘检测
    edges = cv2.Canny(vertical, 50, 150)
    
    return edges

def detect_vertical_lines(edge_image):
    """使用霍夫变换检测垂直线条"""
    lines = cv2.HoughLinesP(edge_image, 1, np.pi/180, threshold=50, minLineLength=100, maxLineGap=20)
    
    vertical_lines = []
    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            # 计算线的角度
            angle = np.abs(np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi)
            
            # 如果接近垂直(90度)
            if angle > 75 and angle < 105:
                # 存储垂直线的x坐标(取中点)
                vertical_lines.append((x1 + x2) // 2)
    
    # 对垂直线的x坐标排序
    vertical_lines.sort()
    return vertical_lines

def extract_tube_positions_from_lines(vertical_lines, image_width):
    """从垂直线提取试管位置"""
    # 去除重复的线(相距太近的线可能是同一条线的重复检测)
    filtered_lines = []
    prev_x = -30  # 初始设置为一个负值
    
    for x in vertical_lines:
        if x - prev_x > 20:  # 如果与前一条线距离大于20像素
            filtered_lines.append(x)
            prev_x = x
    
    # 如果线太少，添加图像左右边界
    if filtered_lines and filtered_lines[0] > 30:
        filtered_lines.insert(0, 0)
    if filtered_lines and filtered_lines[-1] < image_width - 30:
        filtered_lines.append(image_width)
    
    # 将垂直线分组形成试管边界
    tube_positions = []
    for i in range(len(filtered_lines) - 1):
        left = filtered_lines[i]
        right = filtered_lines[i + 1]
        
        # 忽略太窄的间隔
        if right - left < 20:
            continue
            
        tube_positions.append((left, right))
    
    return tube_positions

def verify_tube(roi):
    """验证区域是否可能是试管"""
    # 转为灰度图
    gray = cv2.cvtColor(roi, cv2.COLOR_RGB2GRAY)
    
    # 计算高宽比
    height, width = roi.shape[:2]
    aspect_ratio = height / width
    
    # 试管通常是细长的
    if aspect_ratio < 2.0:  # 不是细长物体
        return False
    
    # 检查垂直边缘 - 试管通常有明显的垂直边缘
    edges = cv2.Canny(gray, 50, 150)
    lines = cv2.HoughLinesP(edges, 1, np.pi/180, threshold=30, minLineLength=height//3, maxLineGap=10)
    
    if lines is None or len(lines) < 2:
        return False
    
    # 检查是否有足够的垂直线
    vertical_count = 0
    for line in lines:
        x1, y1, x2, y2 = line[0]
        angle = np.abs(np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi)
        if angle > 75 and angle < 105:
            vertical_count += 1
    
    return vertical_count >= 2  # 至少需要两条垂直线

def extract_tubes_from_segmentation(seg_result, image):
    """从分割结果中提取试管"""
    try:
        masks = seg_result.masks
        if masks is None:
            return None
            
        # 获取分割掩码和图像尺寸
        height, width = image.shape[:2]
        annotated_image = image.copy()
        tube_data = []
        
        # 对每个掩码进行处理
        for i, mask_tensor in enumerate(masks.data):
            # 转换掩码为numpy数组
            mask = mask_tensor.cpu().numpy()
            
            # 计算边界框
            y_indices, x_indices = np.where(mask > 0.5)
            if len(y_indices) == 0 or len(x_indices) == 0:
                continue
                
            x1, y1 = np.min(x_indices), np.min(y_indices)
            x2, y2 = np.max(x_indices), np.max(y_indices)
            
            # 忽略太小的区域
            if (x2-x1) < 20 or (y2-y1) < 50:
                continue
                
            # 裁剪试管区域
            tube_roi = image[y1:y2, x1:x2]
            
            # 使用增强的属性分析
            from models.model_handler import analyze_tube_properties
            properties = analyze_tube_properties(tube_roi, {'main': None})  # 使用传统方法
            
            # 获取试管属性
            tube_length = properties.get('tube_length', y2 - y1)
            cap_color = properties.get('cap_color', '未知')
            liquid_position = properties.get('liquid_position', '未知')
            label_text = properties.get('label_text', '未能识别')
            
            # 在图像上标注分割结果
            # 创建彩色掩码
            color_mask = np.zeros((height, width, 3), dtype=np.uint8)
            color = (0, 255, 0)  # 绿色
            color_mask[mask > 0.5] = color
            
            # 透明度混合
            alpha = 0.3
            cv2.addWeighted(color_mask, alpha, annotated_image, 1-alpha, 0, annotated_image)
            
            # 画边界框
            cv2.rectangle(annotated_image, (x1, y1), (x2, y2), color, 2)
            
            # 添加标签
            cv2.putText(annotated_image, f"ID:{i+1} {cap_color}", (x1, y1-10), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
            
            # 添加液位信息       
            cv2.putText(annotated_image, liquid_position, (x1, y1+20), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1)
            
            # 存储试管数据
            tube_data.append({
                'id': i+1,
                'length': tube_length,
                'cap_color': cap_color,
                'liquid_position': liquid_position,
                'label_text': label_text,
                'confidence': float(seg_result.boxes.conf[i].item()) if i < len(seg_result.boxes) else 0.9,
                'bbox': [x1, y1, x2, y2]
            })
            
        return {'image': annotated_image, 'tubes': tube_data}
        
    except Exception as e:
        print(f"提取分割试管失败: {str(e)}")
        return None 